using System.Runtime.InteropServices;
using System.Windows.Forms;
using Win32;
using Win32.Hooks;

namespace Vin.Win32.Hooks;

public class VinMouseHook : HookBase
{
    public delegate int MouseEventHandler(object? sender, MouseEventArgs e);

    private uint _doubleClickTime;
    private bool _mouseLeftDown;
    private int _lastMouseDownTimeSpan;

    public event MouseEventHandler? LeftDown;

    public event MouseEventHandler? LeftUp;

    public event MouseEventHandler? MiddleDown;

    public event MouseEventHandler? MiddleUp;

    public event MouseEventHandler? RightDown;

    public event MouseEventHandler? RightUp;

    public event MouseEventHandler? MouseWheel;

    public event MouseEventHandler? MouseMove;

    protected override int HookID => 14;

    public VinMouseHook() => this._doubleClickTime = User32.GetDoubleClickTime();

    public override void Stop() => base.Stop();

    public override int HookProcEvent(int nCode, int wParam, IntPtr lParam)
    {
        int result = 0;

        if (nCode >= 0)
        {
            int delta1 = 0;
            var structure =
                (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct))!;

            result = wParam switch
            {
                512 => MouseMove?.Invoke(this,
                    new MouseEventArgs(MouseButtons.None, 0, structure.pt.X, structure.pt.Y, delta1)) ?? 0,
                513 => LeftDown?.Invoke(this,
                    new MouseEventArgs(MouseButtons.Left, 1, structure.pt.X, structure.pt.Y, delta1)) ?? 0,
                514 => LeftUp?.Invoke(this,
                    new MouseEventArgs(MouseButtons.Left, 1, structure.pt.X, structure.pt.Y, delta1)) ?? 0,
                516 => RightDown?.Invoke(this,
                    new MouseEventArgs(MouseButtons.Right, 1, structure.pt.X, structure.pt.Y, delta1)) ?? 0,
                517 => RightUp?.Invoke(this,
                    new MouseEventArgs(MouseButtons.Right, 1, structure.pt.X, structure.pt.Y, delta1)) ?? 0,
                519 => MiddleDown?.Invoke(this,
                    new MouseEventArgs(MouseButtons.Middle, 1, structure.pt.X, structure.pt.Y, delta1)) ?? 0,
                520 => MiddleUp?.Invoke(this,
                    new MouseEventArgs(MouseButtons.Middle, 1, structure.pt.X, structure.pt.Y, delta1)) ?? 0,
                522 => !this._mouseLeftDown
                    ? MouseWheel?.Invoke(this,
                        new MouseEventArgs(MouseButtons.Middle, 0, structure.pt.X, structure.pt.Y,
                            structure.hWnd >> 16)) ?? 0
                    : 0,
                _ => 0,
            };

            // switch (wParam)
            // {
            //     case 512:
            //         result = MouseMove?.Invoke(this,
            //             new MouseEventArgs(MouseButtons.None, 0, structure.pt.X, structure.pt.Y, delta1)) ?? 0;
            //         break;
            //     case 513:
            //         _lastMouseDownTimeSpan = Environment.TickCount;
            //         MouseEventArgs e2 =
            //             new MouseEventArgs(MouseButtons.Left, 1, structure.pt.X, structure.pt.Y, delta1);
            //         result = LeftDown?.Invoke(this, e2) ?? 0;
            //         break;
            //     case 514:
            //         this._lastMouseDownTimeSpan = Environment.TickCount;
            //         MouseEventArgs e3 =
            //             new MouseEventArgs(MouseButtons.Left, 1, structure.pt.X, structure.pt.Y, delta1);
            //         result = LeftUp?.Invoke(this, e3) ?? 0;
            //         break;
            //     case 516:
            //         MouseEventArgs e4 =
            //             new MouseEventArgs(MouseButtons.Right, 1, structure.pt.X, structure.pt.Y, delta1);
            //         result = RightDown?.Invoke(this, e4) ?? 0;
            //         break;
            //     case 517:
            //         MouseEventArgs e5 =
            //             new MouseEventArgs(MouseButtons.Right, 1, structure.pt.X, structure.pt.Y, delta1);
            //         result = RightUp?.Invoke(this, e5) ?? 0;
            //         break;
            //     case 519:
            //         MouseEventArgs e6 =
            //             new MouseEventArgs(MouseButtons.Middle, 1, structure.pt.X, structure.pt.Y, delta1);
            //         result = MiddleDown?.Invoke(this, e6) ?? 0;
            //         break;
            //     case 520:
            //         MouseEventArgs e7 =
            //             new MouseEventArgs(MouseButtons.Middle, 1, structure.pt.X, structure.pt.Y, delta1);
            //         result = MiddleUp?.Invoke(this, e7) ?? 0;
            //         break;
            //     case 522:
            //         if (!this._mouseLeftDown)
            //         {
            //             int clicks = 0;
            //             int delta2 = structure.hWnd >> 16;
            //             MouseEventArgs e8 = new MouseEventArgs(MouseButtons.Middle, clicks, structure.pt.X,
            //                 structure.pt.Y, delta2);
            //
            //             result = MouseWheel?.Invoke(this, e8) ?? 0;
            //         }
            //
            //         break;
            // }
        }

        // 如果你想阻止鼠标事件继续传递，返回 1
        // 否则，返回 CallNextHookEx 函数的结果
        return result == 0 ? CallNextHookEx(this.hwndHook, nCode, wParam, lParam) : 1;
    }

    public static uint HiWord(IntPtr ptr)
    {
        return ((int)(uint)ptr & int.MinValue) == int.MinValue
            ? (uint)ptr >> 16
            : (uint)ptr >> 16 & (uint)ushort.MaxValue;
    }

    private struct MouseHookStruct
    {
        public POINT pt;
        public int hWnd;
        public int wHitTestCode;
        public int dwExtraInfo;
    }
}